#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <fcntl.h>
#include <string.h>
#include <sys/stat.h>

#define read_lock(fd, offset, whence, len) \
	lock_reg((fd), F_SETLK, F_RDLCK, (offset), (whence), (len))
#define readw_lock(fd, offset, whence, len) \
	lock_reg((fd), F_SETLKW, F_RDLCK, (offset), (whence), (len))
#define write_lock(fd, offset, whence, len) \
	lock_reg((fd), F_SETLK, F_WRLCK, (offset), (whence), (len))
#define writew_lock(fd, offset, whence, len) \
	lock_reg((fd), F_SETLKW, F_WRLCK, (offset), (whence), (len))
#define un_lock(fd, offset, whence, len) \
	lock_reg((fd), F_SETLK, F_UNLCK, (offset), (whence), (len))

int lock_reg(int fd, int cmd, int type, off_t offset, int whence, off_t len)
{
	struct flock lock;

	lock.l_type = type;
	lock.l_start = offset;
	lock.l_whence = whence;
	lock.l_len = len;

	return (fcntl(fd, cmd, &lock));
}

pid_t lock_test(int fd, int type, off_t offset, int whence, off_t len)
{
	struct flock lock;
	lock.l_type = type;
	lock.l_start = offset;
	lock.l_whence = whence;
	lock.l_len = len;

	if (fcntl(fd, F_GETLK, &lock) < 0)
	{
		printf("fcntl error\n");
		exit(-1);
	}

	if (lock.l_type == F_UNLCK)
		return (0);
	return (lock.l_pid);
}

const char* msg[]
{
	"derived process writing\n",
	"base process write 1st\n",
	"base process write 2nd\n",
};

int main()
{
	int fd = open("data.txt", O_RDWR|O_APPEND|O_TRUNC|O_CREAT);
	struct stat statbuf;
	fstat(fd, &statbuf);
	fchmod(fd, (statbuf.st_mode & ~S_IXGRP) | S_ISGID);
	bool tryagain = true;
	int ret;
	if (fork() == 0)
	{
		while ((ret = write_lock(fd, 0, SEEK_SET, 0)) == -1)
		{
			printf("derived process write lock error\n");
			printf("errno = %d\n", errno);
			sleep(3);
			if (tryagain == false)
				break;
			tryagain = false;
		}
		printf("derived process get lock? %d\n", ret);
		write(fd, msg[0], strlen(msg[0]));
		sleep(2);
		printf("derived process exited.\n");
	}
	else
	{
		while ((ret = write_lock(fd, 0, SEEK_SET, 0)) == -1)
		{
			printf("base process write lock error\n");
			printf("errno = %d\n", errno);
			sleep(3);
			if (tryagain == false)
				break;
			tryagain = false;
		}
		printf("get lock? %d\n", ret);
		write(fd, msg[1], strlen(msg[1]));
		sleep(20);
		write(fd, msg[2], strlen(msg[2]));
		printf("exited\n");
	}
	return 0;
}
